
// Copyright (C) 2009-2011 Luca Piccioni
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//  
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
//  
// You should have received a copy of the GNU General Public License
// along with this program.  If not, see <http://www.gnu.org/licenses/>.

using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Reflection;

using Derm.OpenGL;
using Derm.Raster;

namespace Derm.Render
{
	/// <summary>
	/// Rendering framebuffer.
	/// </summary>
	/// <remarks>
	/// </remarks>
	public sealed class RenderFramebuffer : RenderSurface
	{
		#region Texture Attachment (Render-To-Texture)

		/// <summary>
		/// Utility structure for associating texture and level information.
		/// </summary>
		private struct RenderTextureAttachment
		{
			/// <summary>
			/// The attached texture.
			/// </summary>
			public Texture Texture;

			/// <summary>
			/// The texture level used for attachment.
			/// </summary>
			public uint TextureLevel;

			/// <summary>
			/// Reset this structure.
			/// </summary>
			public void Reset()
			{
				Texture = null;
				TextureLevel = 0;
			}
		}

		#endregion

		#region Color Buffer Attachment

		/// <summary>
		/// Attach a render buffer image to color buffer.
		/// </summary>
		/// <param name="buffer">
		/// A <see cref="RenderBuffer"/> which will be used for read/write operation on this RenderFrambuffer.
		/// </param>
		/// <param name="attachment">
		/// A <see cref="UInt32"/> that specifies the framebuffer color attachment index.
		/// </param>
		public void AttachColor(RenderBuffer buffer, uint attachment)
		{
			if (buffer == null)
				throw new ArgumentNullException("buffer");
			if (buffer.BufferType != RenderBuffer.Type.Color)
				throw new ArgumentException("buffer is not a color buffer type");
			if (attachment >= RenderContext.CurrentCaps.MaxColorAttachments)
				throw new ArgumentException("attachment index '"+attachment+"' is greater than the maximum allowed");

			// Reset buffer color attachment
			mColorBuffers[attachment] = buffer;
			// Reset texture color attachment
			mColorTextures[attachment].Reset();
		}

		/// <summary>
		/// Attach a 2D texture image to color buffer.
		/// </summary>
		/// <param name="texture">
		/// A <see cref="Texture2d"/> which will be used for read/write operation on this RenderFrambuffer.
		/// </param>
		public void AttachColor(Texture2d texture)
		{
			AttachColor(texture, 0, 0);
		}

		/// <summary>
		/// Attach a 2D texture image to color buffer.
		/// </summary>
		/// <param name="texture">
		/// A <see cref="Texture2d"/> which will be used for read/write operation on this RenderFrambuffer.
		/// </param>
		/// <param name="attachment">
		/// A <see cref="UInt32"/> that specifies the framebuffer color attachment index.
		/// </param>
		public void AttachColor(Texture2d texture, uint attachment)
		{
			AttachColor(texture, 0, attachment);
		}

		/// <summary>
		/// Attach a 2D texture image to color buffer.
		/// </summary>
		/// <param name="texture">
		/// A <see cref="Texture"/> which will be used for read/write operation on this RenderFrambuffer.
		/// </param>
		/// <param name="level">
		/// A <see cref="UInt32"/> that specifies the texture level to bind.
		/// </param>
		/// <param name="attachment">
		/// A <see cref="UInt32"/> that specifies the framebuffer color attachment index.
		/// </param>
		public void AttachColor(Texture2d texture, uint level, uint attachment)
		{
			if ((level != 0) && (level > (uint)Math.Log((double)level, 2.0)))
				throw new ArgumentException("level greater than maximum allowed");

			AttachColor((Texture)texture, level, attachment);
		}

		/// <summary>
		/// Attach a rectangle texture image to color buffer.
		/// </summary>
		/// <param name="texture">
		/// A <see cref="Texture"/> which will be used for read/write operation on this RenderFrambuffer.
		/// </param>
		public void AttachColor(TextureRectangle texture)
		{
			AttachColor(texture, 0);
		}

		/// <summary>
		/// Attach a rectangle texture image to color buffer.
		/// </summary>
		/// <param name="texture">
		/// A <see cref="Texture"/> which will be used for read/write operation on this RenderFrambuffer.
		/// </param>
		/// <param name="attachment">
		/// A <see cref="UInt32"/> that specifies the framebuffer color attachment index.
		/// </param>
		public void AttachColor(TextureRectangle texture, uint attachment)
		{
			AttachColor(texture, 0, attachment);
		}

		/// <summary>
		/// Attach a texture image to color buffer.
		/// </summary>
		/// <param name="texture">
		/// A <see cref="Texture"/> which will be used for read/write operation on this RenderFrambuffer.
		/// </param>
		/// <param name="level">
		/// A <see cref="UInt32"/> that specifies the texture level to bind.
		/// </param>
		/// <param name="attachment">
		/// A <see cref="UInt32"/> that specifies the framebuffer color attachment index.
		/// </param>
		private void AttachColor(Texture texture, uint level, uint attachment)
		{
			if (texture == null)
				throw new ArgumentNullException("texture");
			if (texture.HasValidTarget == false)
				throw new ArgumentException("texture has not valid target");
			if (attachment >= RenderContext.CurrentCaps.MaxColorAttachments)
				throw new ArgumentException("attachment index '"+attachment+"' is greater than the maximum allowed");

			switch (Pixel.GetPixelSpace(texture.PixelFormat)) {
				case Pixel.Space.Rgb:
				case Pixel.Space.Rgba:
				case Pixel.Space.Bgr:
				case Pixel.Space.Bgra:
				case Pixel.Space.Gray:
				case Pixel.Space.GrayAlpha:
					break;
				default:
					throw new ArgumentException("invalid texture color space", "texture");
			}

			// Collect texture
			RenderTextureAttachment rTextureAttachment;

			rTextureAttachment.Texture = texture;
			rTextureAttachment.TextureLevel = level;

			// Reset color attachment (texture)
			mColorTextures[attachment] = rTextureAttachment;
			// Reset color attachment (buffer)
			mColorBuffers[attachment] = null;
		}

		/// <summary>
		/// Detach a color attachment.
		/// </summary>
		/// <param name="attachment">
		/// A <see cref="UInt32"/> that specifies the texture level to unbind.
		/// </param>
		public void DetachColor(uint attachment)
		{
			if (attachment >= RenderContext.CurrentCaps.MaxColorAttachments)
				throw new ArgumentException("attachment index '"+attachment+"' is greater than the maximum allowed");

			mColorBuffers[attachment] = null;
			mColorTextures[attachment].Texture = null;
		}

		/// <summary>
		/// Detach all color attachment.
		/// </summary>
		public void DetachColors()
		{
			for (uint i = 0; i < (uint)RenderContext.CurrentCaps.MaxColorAttachments; i++)
				DetachColor(i);
		}

		/// <summary>
		/// RenderBuffers used as color attachment.
		/// </summary>
		private readonly RenderBuffer[] mColorBuffers = new RenderBuffer[RenderContext.CurrentCaps.MaxColorAttachments];

		/// <summary>
		/// Textures used as color attachment.
		/// </summary>
		private readonly RenderTextureAttachment[] mColorTextures = new RenderTextureAttachment[RenderContext.CurrentCaps.MaxColorAttachments];

		#endregion

		#region Depth Buffer Attachment

		/// <summary>
		/// 
		/// </summary>
		/// <param name="buffer"></param>
		public void AttachDepth(RenderBuffer buffer)
		{
			if (buffer == null)
				throw new ArgumentNullException("buffer");
			if (buffer.BufferType != RenderBuffer.Type.Depth)
				throw new ArgumentException("buffer is not a color buffer type");

			// Reset buffer color attachment
			mDepthBuffer = buffer;
			// Reset texture color attachment
			mDepthTexture.Reset();
		}

		/// <summary>
		/// 
		/// </summary>
		/// <param name="texture"></param>
		/// <param name="level"></param>
		public void AttachDepth(Texture2d texture, uint level)
		{
			AttachDepth((Texture)texture, level);
		}

		/// <summary>
		/// 
		/// </summary>
		/// <param name="texture"></param>
		/// <param name="level"></param>
		public void AttachDepth(TextureRectangle texture, uint level)
		{
			AttachDepth((Texture)texture, level);
		}

		/// <summary>
		/// 
		/// </summary>
		/// <param name="texture"></param>
		/// <param name="level"></param>
		private void AttachDepth(Texture texture, uint level)
		{
			if (texture == null)
				throw new ArgumentNullException("texture");
			if (texture.HasValidTarget == false)
				throw new ArgumentException("texture has not valid target");

			// Reset buffer color attachment
			mDepthBuffer = null;
			// Reset texture color attachment
			mDepthTexture.Texture = texture;
			mDepthTexture.TextureLevel = level;
		}

		/// <summary>
		/// Detach depth buffer.
		/// </summary>
		public void DetachDepth()
		{
			mDepthBuffer = null;
			mDepthTexture.Reset();
		}

		/// <summary>
		/// RenderBuffer used as depth attachment.
		/// </summary>
		private RenderBuffer mDepthBuffer = null;

		/// <summary>
		/// Texture used as depth attachment.
		/// </summary>
		private RenderTextureAttachment mDepthTexture;

		#endregion

		#region Stencil Buffer Attachment

		/// <summary>
		/// 
		/// </summary>
		/// <param name="buffer"></param>
		public void AttachStencil(RenderBuffer buffer)
		{
			if (buffer == null)
				throw new ArgumentNullException("buffer");
			if (buffer.BufferType != RenderBuffer.Type.Stencil)
				throw new ArgumentException("buffer is not a stencil buffer type");

			// Reset buffer color attachment
			mStencilBuffer = buffer;
		}

		/// <summary>
		/// Detach stencil buffer.
		/// </summary>
		public void DetachStencil()
		{
			mStencilBuffer = null;
		}

		/// <summary>
		/// RenderBuffer used as depth attachment.
		/// </summary>
		private RenderBuffer mStencilBuffer = null;

		#endregion

		#region Framebuffer Grabbing

		/// <summary>
		/// Read this RenderSurface color buffer.
		/// </summary>
		/// <param name="ctx">
		/// A <see cref="RenderContext"/>
		/// </param>
		/// <param name="attachment">
		/// </param>
		/// <param name="x">
		/// A <see cref="System.Int32"/> that specifies the x coordinate of the lower left corder of the rectangle area to read.
		/// </param>
		/// <param name="y">
		/// A <see cref="System.Int32"/> that specifies the y coordinate of the lower left corder of the rectangle area to read.
		/// </param>
		/// <param name="width">
		/// A <see cref="System.Int32"/> that specifies the width of the rectangle area to read.
		/// </param>
		/// <param name="height">
		/// A <see cref="System.Int32"/> that specifies the height of the rectangle area to read.
		/// </param>
		/// <param name="pType">
		/// A <see cref="Pixel.Type"/> which determine the pixel storage of the returned image.
		/// </param>
		/// <returns>
		/// It returns an <see cref="Image"/> representing the current read buffer <paramref name="rBuffer"/>.
		/// </returns>
		public Image ReadColorBuffer(RenderContext ctx, uint attachment, uint x, uint y, uint width, uint height, Pixel.Type pType)
		{
			return (ReadBuffer(ctx, Gl.COLOR_ATTACHMENT0 + (int)attachment, x, y, width, height, pType));
		}

		/// <summary>
		/// Copy this RenderSurface color buffer into a buffer.
		/// </summary>
		/// <param name="ctx">
		/// A <see cref="RenderContext"/>
		/// </param>
		/// <param name="attachment">
		/// </param>
		/// <param name="x">
		/// A <see cref="System.Int32"/> that specifies the x coordinate of the lower left corder of the rectangle area to read.
		/// </param>
		/// <param name="y">
		/// A <see cref="System.Int32"/> that specifies the y coordinate of the lower left corder of the rectangle area to read.
		/// </param>
		/// <param name="texture">
		/// </param>
		/// <param name="level">
		/// </param>
		public void CopyColorBuffer(RenderContext ctx, uint attachment, uint x, uint y, ref Texture texture, uint level)
		{
			CopyBuffer(ctx, Gl.COLOR_ATTACHMENT0 + (int)attachment, x, y, ref texture, level);
		}

		#endregion

		#region RenderSurface Overrides

		/// <summary>
		/// RenderSurface width property. 
		/// </summary>
		public override uint Width
		{
			get {
				uint width = UInt32.MaxValue;

				for (int i = 0; i < RenderContext.CurrentCaps.MaxColorAttachments; i++) {
					if      (mColorBuffers[i] != null)
						width = (uint)Math.Min(width, mColorBuffers[i].Width);
					else if (mColorTextures[i].Texture != null)
						width = (uint)Math.Min(width, mColorTextures[i].Texture.Width);
				}

				return ((width != UInt32.MaxValue) ? width : 0);
			}
		}
		
		/// <summary>
		/// RenderSurface height property. 
		/// </summary>
		public override uint Height
		{
			get {
				uint height = UInt32.MaxValue;

				for (int i = 0; i < RenderContext.CurrentCaps.MaxColorAttachments; i++) {
					if      (mColorBuffers[i] != null)
						height = (uint)Math.Min(height, mColorBuffers[i].Height);
					else if (mColorTextures[i].Texture != null)
						height = (uint)Math.Min(height, mColorTextures[i].Texture.Height);
				}

				return ((height != UInt32.MaxValue) ? height : 0);
			}
		}

		/// <summary>
		/// Get the device context associated to this RenderFramebuffer. 
		/// </summary>
		/// <returns>
		/// It always returns <see cref="IntPtr.Zero"/>, since no device context is related to this render
		/// surface.
		/// </returns>
		public override IDeviceContext GetDeviceContext()
		{
			return (null);
		}

		/// <summary>
		/// Current surface configuration.
		/// </summary>
		/// <remarks>
		/// <para>
		/// This read-only property shall return a <see cref="RenderSurfaceFormat"/> indicating the current
		/// buffer configuration. The object returned shall not be used to modify this RenderSurface buffers,
		/// but it shall be used to know which is the buffer configuration.
		/// </para>
		/// </remarks>
		public override RenderSurfaceFormat BufferFormat
		{
			get
			{
				RenderSurfaceFormat bufferFormat = new RenderSurfaceFormat();

				// It has a color buffer?
				Pixel.Type colorType = Pixel.Type.None;

				if (colorType == Pixel.Type.None) {
					foreach (RenderBuffer renderBuffer in mColorBuffers) {
						if ((renderBuffer != null) && (renderBuffer.BufferType == RenderBuffer.Type.Color)) {
							colorType = Pixel.Type.RGB24;
							break;
						}
					}
				}

				if (colorType == Pixel.Type.None) {
					foreach (RenderTextureAttachment renderTextureAttachment in mColorTextures) {
						if (renderTextureAttachment.Texture != null) {
							break;
						}
					}
				}

				// It has a depth buffer

				// It has a stencil buffer?

				// No double buffering and stereo buffering

				return (bufferFormat);
			}
		}

		/// <summary>
		/// Bind this RenderSurface for drawing.
		/// </summary>
		/// <param name="ctx">
		/// A <see cref="RenderContext"/> to wich associate its rendering result to this RenderSurface.
		/// </param>
		public override void BindDraw(RenderContext ctx)
		{
			// Bind this framebuffer
			Gl.BindFramebuffer(Gl.DRAW_FRAMEBUFFER, ObjectName);
			Debug.Assert(RenderException.CheckErrors() == true);
		}

		/// <summary>
		/// Unbind this RenderSurface for drawing.
		/// </summary>
		/// <param name="ctx">
		/// A <see cref="RenderContext"/> to wich disassociate its rendering result from this RenderSurface.
		/// </param>
		public override void UnbindDraw(RenderContext ctx)
		{
			// Switch to default framebuffer
			Gl.BindFramebuffer(Gl.DRAW_FRAMEBUFFER, InvalidObjectName);
			Debug.Assert(RenderException.CheckErrors() == true);
		}

		/// <summary>
		/// Bind this RenderSurface for reading.
		/// </summary>
		/// <param name="ctx">
		/// A <see cref="RenderContext"/> to wich associate its read result to this RenderSurface.
		/// </param>
		public override void BindRead(RenderContext ctx)
		{
			// Bind this framebuffer
			Gl.BindFramebuffer(Gl.READ_FRAMEBUFFER, ObjectName);
			Debug.Assert(RenderException.CheckErrors() == true);
		}

		/// <summary>
		/// Unbind this RenderSurface for reading.
		/// </summary>
		/// <param name="ctx">
		/// A <see cref="RenderContext"/> to wich disassociate its read result from this RenderSurface.
		/// </param>
		public override void UnbindRead(RenderContext ctx)
		{
			// Switch to default framebuffer
			Gl.BindFramebuffer(Gl.READ_FRAMEBUFFER, InvalidObjectName);
			Debug.Assert(RenderException.CheckErrors() == true);
		}
		
		/// <summary>
		/// Validate this RenderFramebuffer.
		/// </summary>
		/// <param name="ctx">
		/// A <see cref="RenderContext"/> used for validating this RenderFramebuffer.
		/// </param>
		/// <exception cref="InvalidOperationException">
		/// Exception thrown whenever this RenderFramebuffer is not valid.
		/// </exception>
		private void Validate(RenderContext ctx)
		{
			int status;

			// Check frambuffer completeness
			status = Gl.CheckFramebufferStatus(Gl.FRAMEBUFFER);
			RenderException.DebugCheckErrors();
			if (status != Gl.FRAMEBUFFER_COMPLETE) {
				string msg;

				switch (status) {
					case Gl.FRAMEBUFFER_UNDEFINED:
						msg = "undefined";
						break;
					case Gl.FRAMEBUFFER_INCOMPLETE_ATTACHMENT:
						msg = "incomplete attachment";
						break;
					case Gl.FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT:
						msg = "incomplete missing attachment";
						break;
					case Gl.FRAMEBUFFER_INCOMPLETE_READ_BUFFER:
						msg = "incomplete read buffer";
						break;
					case Gl.FRAMEBUFFER_UNSUPPORTED:
						msg = "unsupported";
						break;
					case Gl.FRAMEBUFFER_INCOMPLETE_MULTISAMPLE:
						msg = "incomplete multisample";
						break;
					case Gl.FRAMEBUFFER_INCOMPLETE_LAYER_TARGETS:
						msg = "incomplete layer targets";
						break;
					default:
						msg = "unknown error";
						break;
				}

				throw new RenderException(status, "framebuffer not complete ({0})", msg);
			}
		}

		/// <summary>
		/// Determine whether this surface has to be swapped.
		/// </summary>
		/// <remarks>
		/// This routine returns always 'false', since the framebuffer cannot be defined with a double buffer.
		/// </remarks>
		public override bool Swappable { get { return (false); } }

		/// <summary>
		/// Swap render surface. 
		/// </summary>
		/// <exception cref="InvalidOperationException">
		/// This exception is always thrown since <see cref="Swappable"/> returns always 'false'.
		/// </exception>
		/// <remarks>
		/// Do not call this routine.
		/// </remarks>
		public override void SwapSurface()
		{
			throw new InvalidOperationException("framebuffer can't swap");
		}

		/// <summary>
		/// Flag indicating whether framebuffer buffers configuration have changed.
		/// </summary>
		private bool mDirtyBuffers = true;

		#endregion

		#region RenderResource Overrides

		/// <summary>
		/// RenderResource object class.
		/// </summary>
		internal static readonly Guid FramebufferObjectClass = new Guid("7A8F2D14-9A8D-4CD9-BFAD-5E10CA1C33CE");

		/// <summary>
		/// RenderResource object class.
		/// </summary>
		public override Guid ObjectClass { get { return (FramebufferObjectClass); } }

		/// <summary>
		/// Determine whether this RenderFramebuffer really exists for a specific context.
		/// </summary>
		/// <param name="ctx">
		/// A <see cref="RenderContext"/> that would have created (or a sharing one) the object. This context shall be current to
		/// the calling thread.
		/// </param>
		/// <returns>
		/// It returns a boolean value indicating whether this RenderFramebuffer exists in the object space of <paramref name="ctx"/>.
		/// </returns>
		/// <remarks>
		/// <para>
		/// The object existence is done by checking a valid object by its name <see cref="IRenderResource.ObjectName"/>. This routine will test whether
		/// <paramref name="ctx"/> has created this RenderFramebuffer (or is sharing with the creator).
		/// </para>
		/// </remarks>
		/// <exception cref="ArgumentNullException">
		/// Exception thrown if <paramref name="ctx"/> is null.
		/// </exception>
		/// <exception cref="ArgumentException">
		/// Exception thrown if <paramref name="ctx"/> is not current to the calling thread.
		/// </exception>
		public override bool Exists(RenderContext ctx)
		{
			if (ctx == null)
				throw new ArgumentNullException("ctx");
			if (ctx.IsCurrent() == false)
				throw new ArgumentException("not current", "ctx");

			return (Gl.IsFramebuffer(ObjectName));
		}

		/// <summary>
		/// Create a RenderFramebuffer name.
		/// </summary>
		/// <param name="ctx">
		/// A <see cref="RenderContext"/> used for creating this object name.
		/// </param>
		/// <returns>
		/// It returns a valid object name for this RenderResource.
		/// </returns>
		/// <exception cref="NotImplementedException">
		/// Exception always thrown.
		/// </exception>
		protected override uint CreateName(RenderContext ctx)
		{
			uint objectName;

			Gl.GenFramebuffers(1, out objectName);
			RenderException.DebugCheckErrors();

			return (objectName);
		}

		/// <summary>
		/// Actually create this RenderFramebuffer resources.
		/// </summary>
		/// <param name="ctx">
		/// A <see cref="RenderContext"/> used for allocating resources.
		/// </param>
		protected override void CreateObject(RenderContext ctx)
		{
			// Bind frame buffer
			Gl.BindFramebuffer(Gl.FRAMEBUFFER, ObjectName);
			RenderException.DebugCheckErrors();

			// Reset binding points not set
			for (int i = 0; i < RenderContext.CurrentCaps.MaxColorAttachments; i++) {
				if ((mColorBuffers[i] == null) && (mColorTextures[i].Texture == null)) {
					Gl.FramebufferRenderbuffer(Gl.FRAMEBUFFER, Gl.COLOR_ATTACHMENT0 + i, Gl.RENDERBUFFER, 0);
					RenderException.DebugCheckErrors();
				}
			}

			// Attach color render buffers
			for (int i = 0; i < RenderContext.CurrentCaps.MaxColorAttachments; i++) {
				if (mColorBuffers[i] != null) {
					// Create render buffer
					mColorBuffers[i].Create(ctx);
					// Allocate render buffer
					mColorBuffers[i].Allocate(ctx, Width, Height);

					// Attach render buffer
					Gl.FramebufferRenderbuffer(Gl.FRAMEBUFFER, Gl.COLOR_ATTACHMENT0 + i, Gl.RENDERBUFFER, mColorBuffers[i].ObjectName);
					RenderException.DebugCheckErrors();
				}
			}

			for (int i = 0; i < RenderContext.CurrentCaps.MaxColorAttachments; i++) {
				if (mColorTextures[i].Texture != null) {
					// Attach color texture
					Gl.FramebufferTexture2D(Gl.FRAMEBUFFER, Gl.COLOR_ATTACHMENT0 + i, mColorTextures[i].Texture.TextureTarget, mColorTextures[i].Texture.ObjectName, (int)mColorTextures[i].TextureLevel);
					RenderException.DebugCheckErrors();
				}
			}

			// Attach depth render buffer
			if (mDepthBuffer != null) {
				// Create render buffer
				if (mDepthBuffer.Exists(ctx) == false)
					mDepthBuffer.Create(ctx);

				// Attach render buffer
				Gl.FramebufferRenderbuffer(Gl.FRAMEBUFFER, Gl.DEPTH_ATTACHMENT, Gl.RENDERBUFFER, mDepthBuffer.ObjectName);
				RenderException.DebugCheckErrors();
			}

			// Attach stencil render buffer
			if (mStencilBuffer != null) {
				// Create render buffer
				if (mStencilBuffer.Exists(ctx) == false)
					mStencilBuffer.Create(ctx);

				// Attach render buffer
				Gl.FramebufferRenderbuffer(Gl.FRAMEBUFFER, Gl.STENCIL_ATTACHMENT, Gl.RENDERBUFFER, mStencilBuffer.ObjectName);
				RenderException.DebugCheckErrors();
			}

			// Validate completeness status
			Validate(ctx);
		}

		/// <summary>
		/// Delete a RenderFramebuffer name.
		/// </summary>
		/// <param name="ctx">
		/// A <see cref="RenderContext"/> used for deleting this object name.
		/// </param>
		/// <param name="name">
		/// A <see cref="System.UInt32"/> that specifies the object name to delete.
		/// </param>
		protected override void DeleteName(RenderContext ctx, uint name)
		{
			// Delete program
			Gl.DeleteFramebuffers(1, new uint[] { ObjectName });
			RenderException.DebugCheckErrors();
		}

		#endregion
	}
}
